package io.github.uicdb.et.item.curios;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multimap;
import io.github.uicdb.et.*;
import io.github.uicdb.et.item_data.ItemCurioModifierData;
import io.github.uicdb.et.selection.RandomSelection;
import net.minecraft.ChatFormatting;
import net.minecraft.Util;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.ai.attributes.Attribute;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import org.checkerframework.common.value.qual.IntRange;
import org.jetbrains.annotations.NotNull;
import top.theillusivec4.curios.api.SlotContext;
import top.theillusivec4.curios.api.type.capability.ICurio;
import top.theillusivec4.curios.api.type.capability.ICurioItem;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

public abstract class CurioItem extends Item implements ICurioItem {
    @IntRange(from = 0)
    final int randomSelection;

    public CurioItem(Properties properties, int randomSelection) {
        super(properties.stacksTo(1));
        this.randomSelection = randomSelection;
    }

    public boolean hasRandomSelection() {
        return randomSelection > 0;
    }

    @NotNull
    @Override
    public ICurio.DropRule getDropRule(SlotContext slotContext, DamageSource source, int lootingLevel, boolean recentlyHit, ItemStack stack) {
        return ICurio.DropRule.DEFAULT;
    }

    @Override
    public boolean canEquipFromUse(SlotContext slotContext, ItemStack stack) {
        return isInit(stack);
    }

    private boolean isInit(ItemStack stack) {
        return !hasRandomSelection() || stack.getOrDefault(ModItemDataComponents.CURIO_INIT, false);
    }

    @Override
    public @NotNull InteractionResultHolder<ItemStack> use(@NotNull Level level, Player player, @NotNull InteractionHand usedHand) {
        var stack = player.getItemInHand(usedHand);
        if (!stack.getOrDefault(ModItemDataComponents.CURIO_INIT, false) && hasRandomSelection()) {
            var result = ImmutableList.<Holder<RandomSelection>>builderWithExpectedSize(randomSelection);
            List<Holder<RandomSelection>> ignore = new ArrayList<>(randomSelection);
            ignore.add(ModRandomSelections.EMPTY);
            for (int i = 0; i < randomSelection; i++) {
                var selection = ModHelper.randomSelectWithout(ElementalTheory.RANDOM_SELECTION_REGISTRY.holders(), ignore, level.random);
                if (selection != null) {
                    result.add(selection);
                    ignore.add(selection);
                }
            }
            stack.set(ModItemDataComponents.ADDITION_ATTRIBUTE_MODIFIERS, new ItemCurioModifierData(result.build()));
            player.getItemInHand(usedHand).set(ModItemDataComponents.CURIO_INIT, true);
        }
        return InteractionResultHolder.success(stack);
    }

    protected static @NotNull ResourceLocation getSaftyID(SlotContext slotContext) {
        return ResourceLocation.parse(slotContext.identifier() + "_" + "wind_wing" + "_" + slotContext.index());
    }

    protected static @NotNull ResourceLocation getSaftyID(SlotContext slotContext, String fix) {
        return ResourceLocation.parse(slotContext.identifier() + "_" + "wind_wing" + "_" + slotContext.index() + "_" + fix);
    }

    protected static @NotNull AttributeModifier getSlotModifier(SlotContext slotContext, double amount, AttributeModifier.Operation operation) {
        return new AttributeModifier(getSaftyID(slotContext), amount, operation);
    }

    protected static @NotNull AttributeModifier getSlotModifier(int index, SlotContext slotContext, double amount, AttributeModifier.Operation operation) {
        return new AttributeModifier(getSaftyID(slotContext, String.valueOf(index)), amount, operation);
    }

    protected static @NotNull AttributeModifier getSlotModifier(String index, SlotContext slotContext, double amount, AttributeModifier.Operation operation) {
        return new AttributeModifier(getSaftyID(slotContext, index), amount, operation);
    }

    public static MutableComponent translate(String id) {
        return Component.translatable("text.elemental_theory." + id);
    }

    public abstract void appendDefaultAttribute(Multimap<Holder<Attribute>, AttributeModifier> map, SlotContext slotContext, ResourceLocation id, ItemStack stack);

    @Override
    public Multimap<Holder<Attribute>, AttributeModifier> getAttributeModifiers(SlotContext slotContext, ResourceLocation id, ItemStack stack) {
        var map = HashMultimap.<Holder<Attribute>, AttributeModifier>create();
        appendDefaultAttribute(map, slotContext, id, stack);
        var addition = stack.get(ModItemDataComponents.ADDITION_ATTRIBUTE_MODIFIERS);
        if (addition != null) {
            for (Holder<RandomSelection> entry : addition.entries()) {
                if (entry.isBound()) {
                    var selection = entry.value();
                    map.put(selection.attribute(), new AttributeModifier(ResourceLocation.parse(slotContext.identifier() + "_" + entry.value().id() + "_" + slotContext.index()), selection.amount(), selection.operation()));
                }
            }
        }
        return map;
    }


    @Override
    public List<Component> getAttributesTooltip(List<Component> tooltips, ItemStack stack) {
        if (!isInit(stack)) {
            tooltips.add(Component.translatable("text.elemental_theory.curio_init").withStyle(ChatFormatting.GOLD));
        }
        addAdditionTooltip(tooltips, stack);
        return tooltips;
    }

    public abstract void addAdditionTooltip(List<Component> tooltips, ItemStack stack);

    @Override
    public boolean canEquip(SlotContext slotContext, ItemStack stack) {
        return !slotContext.entity().getData(ModAttachments.EQUIPPED_TAG_HOLDER).has(getEquippedTag());
    }

    @Override
    public void onEquip(SlotContext slotContext, ItemStack prevStack, ItemStack stack) {
        slotContext.entity().getData(ModAttachments.EQUIPPED_TAG_HOLDER).add(getEquippedTag());
    }

    @Override
    public void onUnequip(SlotContext slotContext, ItemStack newStack, ItemStack stack) {
        slotContext.entity().getData(ModAttachments.EQUIPPED_TAG_HOLDER).remove(getEquippedTag());
    }

    Function<ResourceLocation, TagKey<Item>> cache;

    public TagKey<Item> getEquippedTag() {
        if (cache == null) {
            cache = Util.memoize(this::getKey);
        }
        return cache.apply(BuiltInRegistries.ITEM.getKey(this));
    }

    private TagKey<Item> getKey(ResourceLocation location) {
        return TagKey.create(BuiltInRegistries.ITEM.key(), ResourceLocation.fromNamespaceAndPath(location.getNamespace(),"curio/"+location.getPath()));
    }

}
